﻿using System;
using System.Net;
using System.Threading.Tasks;

using DotNetty.Codecs;
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels.Sockets;

namespace SimbaRGVMonitor.Common.Net
{
    public sealed partial class MonitorTcpClient
    {
        public MonitorTcpClient()
        {
            InitializeSettings();
        }

        //====================================================================================================
        //以上是类型构造函数
        //----------------------------------------------------------------------------------------------------
        //以下是类型字段
        //====================================================================================================

        private IChannel channel;
        private int lengthFieldLength = 4;
        private int lengthFieldOffset = 4;
        private int maxFrameLength = ushort.MaxValue;
        private readonly Bootstrap bootstrap = new Bootstrap();
        private readonly IEventLoopGroup group = new MultithreadEventLoopGroup();

        //====================================================================================================
        //以上是类型字段
        //----------------------------------------------------------------------------------------------------
        //以下是类型属性
        //====================================================================================================

        public int MaxFrameLength
        {
            get { return maxFrameLength; }
        }

        public int LengthFieldLength
        {
            get { return lengthFieldLength; }
            set { lengthFieldLength = value; }
        }

        public int LengthFieldOffset
        {
            get { return lengthFieldOffset; }
            set { lengthFieldOffset = value; }
        }

        public bool Connected
        {
            get { return channel != null && channel.Active; }
        }

        public IPEndPoint RemoteAddress
        {
            get { return channel.RemoteAddress as IPEndPoint; }
        }

        public IPEndPoint LocalAddress
        {
            get { return channel.LocalAddress as IPEndPoint; }
        }

        //====================================================================================================
        //以上是类型属性
        //----------------------------------------------------------------------------------------------------
        //以下是类型事件及触发函数
        //====================================================================================================

        public event EventHandler<MonitorTcpClientEventArgs> ConnectStateChanged;
        private void OnConnectStateChanged()
        {
            var e = new MonitorTcpClientEventArgs(this);
            ConnectStateChanged.TriggerEvent(this, e);
        }

        public event EventHandler<MonitorTcpClientDataReceivedEventArgs> DataReceived;
        private void OnDataReceived(byte[] bytes)
        {
            var receiveTime = DateTime.Now;
            var e = new MonitorTcpClientDataReceivedEventArgs(this, receiveTime, bytes);
            DataReceived.TriggerEvent(this, e);
        }

        public event EventHandler<MonitorTcpClientExceptionCaughtEventArgs> ExceptionCaught;
        private void OnExceptionCaught(Exception ex)
        {
            var e = new MonitorTcpClientExceptionCaughtEventArgs(this, ex);
            ExceptionCaught.TriggerEvent(this, e);
        }

        //====================================================================================================
        //以上是类型事件及触发函数
        //----------------------------------------------------------------------------------------------------
        //以下是初始化设置
        //====================================================================================================

        private void InitializeSettings()
        {
            var channelHandler = new ChannelHandler();
            channelHandler.ChannelActived += WhenChannelActived;
            channelHandler.ChannelInactived += WhenChannelInactived;
            channelHandler.ChannelDataReceived += WhenChannelDataReceived;
            channelHandler.ChannelExceptionCaught += WhenChannelExceptionCaught;

            bootstrap
                .Group(group)
                .Channel<TcpSocketChannel>()
                .Option(ChannelOption.TcpNodelay, true)
                .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    IChannelPipeline pipeline = channel.Pipeline;

                    pipeline.AddLast("scheduler-framing-enc", new LengthFieldPrepender(lengthFieldLength));
                    pipeline.AddLast("scheduler-framing-dec", new LengthFieldBasedFrameDecoder(int.MaxValue, 0, lengthFieldOffset, 0, lengthFieldLength));
                    pipeline.AddLast("scheduler-frame-client-handler", channelHandler);
                }));
        }

        //====================================================================================================
        //以上是初始化设置
        //----------------------------------------------------------------------------------------------------
        //以下是ChannelHandler事件侦听
        //====================================================================================================

        private void WhenChannelActived(object sender, DotNettyChannelEventArgs e)
        {
            channel = e.Channel;
            OnConnectStateChanged();
        }

        private void WhenChannelInactived(object sender, DotNettyChannelEventArgs e)
        {
            OnConnectStateChanged();
        }

        private void WhenChannelDataReceived(object sender, DotNettyChannelDataReceivedEventArgs e)
        {
            var buffer = e.Data as IByteBuffer;
            var length = buffer.ReadableBytes;
            var bytes = new byte[length];
            buffer.GetBytes(0, bytes);

            OnDataReceived(bytes);
        }

        private void WhenChannelExceptionCaught(object sender, DotNettyChannelExceptionCaughtEventArgs e)
        {
            OnExceptionCaught(e.Ex);
        }

        //====================================================================================================
        //以上是ChannelHandler事件侦听
        //----------------------------------------------------------------------------------------------------
        //以下是建立及断开连接
        //====================================================================================================

        public Task ConnectAsync(string ip, int port)
        {
            var serverIp = IPAddress.Parse(ip);
            return ConnectAsync(serverIp, port);
        }

        public Task ConnectAsync(IPAddress ip, int port)
        {
            var point = new IPEndPoint(ip, port);
            return ConnectAsync(point);
        }

        public Task ConnectAsync(IPEndPoint point)
        {
            return bootstrap.ConnectAsync(point);
        }

        public Task CloseAsync()
        {
            return channel == null 
                ? Task.Factory.StartNew(() => { })
                : channel.CloseAsync().ContinueWith(task => channel = null);
        }

        //====================================================================================================
        //以上是建立及断开连接
        //----------------------------------------------------------------------------------------------------
        //以下是发送数据
        //====================================================================================================

        public Task SendAsync(byte[] bytes)
        {
            var buffer = Unpooled.WrappedBuffer(bytes);
            return channel.WriteAndFlushAsync(buffer);
        }
    }
}
